param([String]$AccessKey, [bool]$FullInv)

$script = {
$WebService = "http://thingtory.azurewebsites.net/InvTools.asmx"
if( !$AccessKey) { $AccessKey = "DEMO" ; Write-Host 'DEMO Mode onyl, no inventory upload possible !' }

Write-Host 'Thingtory WindowsAgent is collecting data...'
$ErrorActionPreference = "SilentlyContinue"

#We use the UUID as Hardware Identifier
$HWID = (Get-WmiObject -namespace "root\cimv2" -class win32_ComputerSystemProduct).UUID

#use the Computername as DisplayName
$Computername = $env:COMPUTERNAME

#Get current Path or use Temp
$pspath = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
if(!$pspath ) { $pspath = $env:TEMP }

#Clean History if FullInventory is required
if($FullInv) 
{
    Remove-Item "$($pspath)\InventoryOld.xml" -Force -ErrorAction SilentlyContinue
    Remove-Item "$($pspath)\InventoryNew.xml" -Force -ErrorAction SilentlyContinue
    Remove-Item "$($pspath)\InventorySchema.xml" -Force -ErrorAction SilentlyContinue
}

#Connect WebService
$svc = New-WebServiceProxy -Uri $WebService -UseDefaultCredential

# Create a new XML File
[System.XML.XMLDocument]$oXMLDocument=New-Object System.XML.XMLDocument

# Create new Objects Node
[System.XML.XMLElement]$oXMLRoot=$oXMLDocument.CreateElement("Objects")

#load Inv Schema
if(!(Test-Path "$($pspath)\InventorySchema.xml")) 
{
    #Download the Schema from WebService
    [System.XML.XMLDocument]$InvSchema = New-Object System.XML.XMLDocument
    $InvSchema.LoadXml($svc.GetInventorySchema($AccessKey, "WindowsClient").OuterXml)
    $InvSchema.Save("$($pspath)\InventorySchema.xml")
}

#Load the Inventory Schema
[xml]$xml = Get-Content "$($pspath)\InventorySchema.xml"

#Load WMI Objects (other types may be implemented later)
$xml.SelectNodes("//Objects/Object[@Type='System.Management.ManagementObject']/WMI") | % {
$result = Get-WmiObject -Namespace $_.Namespace -Query $_.WQLQuery -ErrorAction Continue | ConvertTo-Xml
    foreach($object in $result.SelectNodes("//Object"))
    {
    try
    {
    #$object = $result.SelectSingleNode("//Object");
    $object.SetAttribute("GroupID", $_.ParentNode.GroupID)
    $object.SetAttribute("Instance", $object.SelectSingleNode("Property[@Name='__RELPATH']").InnerText)

    #Cleanup unneeded Nodes
    foreach($n in ($object.SelectNodes("Property[contains(@Type,'System.Management.')]"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[contains(@Type,'System.ComponentModel.')]"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[starts-with(@Name,'__')]"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[@Name='PSComputerName']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }

    #Import Objects into new XML
    [System.XML.XMLElement]$oXMLSystem=$oXMLRoot.appendChild($oXMLDocument.ImportNode($object, $true))

    # Append as child to an existing node
    $oXMLDocument.appendChild($oXMLRoot) | Out-Null
    } catch{}
    }
}

#Load PS Objects
$xml.SelectNodes("//Objects/Object[@Type='System.Management.Automation.PSCustomObject']/PS") | % {
$result = Invoke-Expression -Command $_.PSCode -ErrorAction SilentlyContinue
    foreach($object in $result.SelectNodes("//Object"))
    {
    try
    {
    #$object = $result.SelectSingleNode("//Object");
    $object.SetAttribute("GroupID", $_.ParentNode.GroupID)
    $object.SetAttribute("Instance", $object.SelectSingleNode("Property[@Name='PSPath']").InnerText)

    #Cleanup unneeded Nodes
    foreach($n in ($object.SelectNodes("Property[@Name='PSChildName']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[@Name='PSDrive']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[@Name='PSProvider']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[@Name='PSParentPath']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }
    foreach($n in ($object.SelectNodes("Property[@Name='PSPath']"))) { $n.ParentNode.RemoveChild($n) | Out-Null }

    #Import Objects into new XML
    [System.XML.XMLElement]$oXMLSystem=$oXMLRoot.appendChild($oXMLDocument.ImportNode($object, $true))

    # Append as child to an existing node
    $oXMLDocument.appendChild($oXMLRoot) | Out-Null
    } catch{}
    }
}


#delete old History and rename latest Inventory file 
if(Test-Path "$($pspath)\InventoryNew.xml")
{
    Remove-Item "$($pspath)\InventoryOld.xml" -Force -ErrorAction SilentlyContinue
    Rename-Item "$($pspath)\InventoryNew.xml" "$($pspath)\InventoryOld.xml" -ErrorAction SilentlyContinue
    $oXMLRoot.SelectSingleNode("//Objects").SetAttribute("Status", "Delta");
}
else
{
    #Update Asset with DisplayName
    $svc.CreateNewAsset($AccessKey, $HWID , $Computername)  
    $oXMLRoot.SelectSingleNode("//Objects").SetAttribute("Status", "Full");
}

#Save Inventory data
$oXMLDocument.Save("$($pspath)\InventoryNew.xml")

#Load current and History XML
[xml]$xmlold = Get-Content "$($pspath)\InventoryOld.xml" -ErrorAction SilentlyContinue
[xml]$xmlnew = Get-Content "$($pspath)\InventoryNew.xml" -ErrorAction SilentlyContinue

#Set Action on all History Nodes to "Remove"
$xmlold.SelectNodes("//Objects/Object/Property") | % { $_.SetAttribute("Action","Remove") } 
#Set Action on all Current Nodes to "Add"
$xmlnew.SelectNodes("//Objects/Object/Property") | % { $_.SetAttribute("Action","Add")}

#Detect delta and set Action on changed content to "Update"
$xmlold.SelectNodes("//Objects/Object/Property") | % { 
    $n = $xmlnew.SelectSingleNode("//Objects/Object[@Instance = '$($_.ParentNode.Instance)' and @GroupID='$($_.ParentNode.GroupID)']/Property[@Name = '$($_.Name)']"); 
    if($n -eq $null) 
    {$r = $xmlnew.SelectSingleNode("//Objects").AppendChild($xmlnew.ImportNode($_.ParentNode, $true))}
    else {
    if( $n.Attributes.GetNamedItem('Action').value -eq 'Add')
    {
    $p = $n.ParentNode; 
    if($n.InnerText -eq $_.InnerText) { 
        $p.RemoveChild($n)  | Out-Null; 
        if(!$p.HasChildNodes) { $p.ParentNode.RemoveChild($p)  | Out-Null }
    } else { $p = $_ ;
    if($n.Name -ne $_.Name) { $xmlnew.SelectSingleNode("//Objects/Object").AppendChild($xmlnew.ImportNode($_, $true))} else { $n.SetAttribute("Action","Update") }}
    }
    }
} | Out-Null 

#Save the delta inventory
if($xmlnew.Objects.HasChildNodes)
{
    $xmlnew.Save("$($pspath)\Delta.xml")
}
else
{
    Remove-Item "$($pspath)\Delta.xml" -Force -ErrorAction SilentlyContinue
    exit 0
}

#Compress delta
$ms = New-Object System.IO.MemoryStream
$cs = New-Object System.IO.Compression.GZipStream($ms, [System.IO.Compression.CompressionMode]::Compress)
$sw = New-Object System.IO.StreamWriter($cs)
$sw.Write($xmlnew.OuterXml)
$sw.Close();

$bytes = $ms.ToArray()
$zipdelta = [System.Convert]::ToBase64String($bytes)

# Create a new XML File
[System.XML.XMLDocument]$zXMLDocument=New-Object System.XML.XMLDocument
$zXMLDocument.PreserveWhitespace = $true;

# Create new Objects Node
[System.XML.XMLElement]$zXMLRoot=$zXMLDocument.CreateElement("GZIP")
$zXMLRoot.InnerText = $zipdelta
$zXMLDocument.appendChild($zXMLRoot) | Out-Null

$zXMLDocument.Save("$($pspath)\DeltaZip.xml")

#Upload data
$bres = $svc.UploadInventory($AccessKey, $HWID, '<?xml version="1.0" encoding="utf-8"?>' + $zXMLDocument.OuterXml)

#if upload failed: Rename new Inv to Old to enforce a Full inventory on next cycle...
if(!$bres) {  Remove-Item "$($pspath)\InventoryOld.xml" -Force -ErrorAction SilentlyContinue ; Rename-Item "$($pspath)\InventoryNew.xml" "$($pspath)\InventoryOld.xml" -ErrorAction SilentlyContinue;}
Write-Host 'Inventory uploaded: ' $bres
}
invoke-command $script
sleep(1)
